package com.md.config.filter;

import com.md.base.BaseFilter;
import com.md.base.exception.ErrorMessage;
import com.md.base.exception.ServerException;
import com.md.client.OpenApiClient;
import com.md.client.OpenOauthClient;
import com.md.client.OpenUsersClient;
import com.md.client.helper.OauthFilterAfter;
import com.md.config.MacClientConfiguration;
import com.md.response.ApiResponse;
import com.md.response.ReplacementTokenResponse;
import com.md.response.UsersResponse;
import com.md.util.DateUtils;
import com.md.util.ObjectUtil;
import com.md.util.SpringContextUtil;
import com.md.util.StringUtil;
import com.md.value.ErrorEnum;
import com.md.value.OauthConstants;
import com.md.value.constants.DefaultServerContext;
import com.md.value.enums.AuthEnum;
import com.md.value.server.ServerContext;
import io.vavr.control.Try;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ObjectUtils;

import javax.servlet.FilterChain;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Optional;

/**
 * 鉴权的过滤器
 *
 * @author 刘骄阳
 * 2022-04-06 17:14
 */
@WebFilter({"/api/*"})
@Slf4j
public class OauthFilter extends BaseFilter {
  private final MacClientConfiguration macClientConfiguration;

  public OauthFilter(final MacClientConfiguration macClientConfiguration) {
    super(9);
    this.macClientConfiguration = macClientConfiguration;
  }


  /**
   * Same contract as for {@code doFilter}, but guaranteed to be
   * just invoked once per request within a single request thread.
   * See {@link #shouldNotFilterAsyncDispatch()} for details.
   * <p>Provides HttpServletRequest and HttpServletResponse arguments instead of the
   * default ServletRequest and ServletResponse ones.
   *
   * @param request     request
   * @param response    response
   * @param filterChain filterChain
   */
  @Override
  protected void doFilterInternal(final @NonNull HttpServletRequest request,
                                  final @NonNull HttpServletResponse response,
                                  final @NonNull FilterChain filterChain) throws IOException {
    try {
      log.info("鉴权过滤器执行~~~");
      String contextPath = request.getRequestURI();
      String noPermissionRequired =
        macClientConfiguration.getPermitAllAntMatchers().stream().filter(contextPath::startsWith)
          .findFirst().orElse(null);
      if (ObjectUtil.notEmpty(noPermissionRequired)) {
        filterChain.doFilter(request, response);
        return;
      }

      // 根据url查询api信息
      ApiResponse apiResponse = OpenApiClient.queryApi(contextPath, request.getMethod());
      // 判断是否需要鉴权
      AuthEnum authEnum = Optional.ofNullable(apiResponse).map(ApiResponse::getAuth).orElse(AuthEnum.OPEN);
      // 获取token
      String token = request.getHeader(OauthConstants.HEADER_TOKEN);
      //  校验是否需要token
      switch (authEnum) {
        case NEED:
          if (StringUtils.isBlank(token)) {
            // 没有token提示去登录
            errorEnum(response, ErrorEnum.UNAUTHORIZED);
            return;
          }
          break;
        case PROHIBIT:
          errorEnum(response, ErrorEnum.PERMISSION);
          return;
        default:
          break;
      }
      if (StringUtils.isNotBlank(token)) {
        // 如果token不存在,返回需要token
        ReplacementTokenResponse replacement = OpenOauthClient.replacement(token);
        log.info("token信息为: {}", StringUtil.objectToJson(replacement));
        // 查询用户信息
        UsersResponse user = OpenUsersClient.queryUserInfo(replacement.getUserId());
        LocalDateTime frozen = user.getFrozen();
        LocalDateTime now = LocalDateTime.now();
        if (!ObjectUtils.isEmpty(frozen) && frozen.isAfter(now)) {
          errorEnum(response, ErrorMessage.of(ErrorEnum.USER_UNAVAILABLE, DateUtils.timeDifference(frozen, now)));
          return;
        }

        // 判断是否需要校验权限
        if (AuthEnum.NEED.equals(authEnum)) {
          // 拿到用户信息,判断用户角色是否拥有权限
          Boolean hasAuth = OpenUsersClient.checkPermission(user.getId(), apiResponse.getId());
          if (ObjectUtil.unEquals(true, hasAuth)) {
            errorEnum(response, ErrorEnum.PERMISSION);
            return;
          }
        }
        ServerContext.put(DefaultServerContext.USER_ID, replacement.getUserId());
        ServerContext.put(DefaultServerContext.TENANT_ID, replacement.getTenantId());
        ServerContext.put(DefaultServerContext.BRAND_ID, replacement.getBrandId());
        Optional.ofNullable(Try.of(() -> SpringContextUtil.getBean(OauthFilterAfter.class))
          .getOrElse(() -> null)).ifPresent(tokenFilterAfter -> tokenFilterAfter.filterAfter(replacement));
      }
      filterChain.doFilter(request, response);
    } catch (Exception e) {
      log.error("执行异常: {}", e.getMessage(), e);
      if (e instanceof ServerException) {
        ServerException serverException = (ServerException) e;
        errorEnum(response, ErrorMessage.of(serverException.getCode(), serverException.getMessage()));
        return;
      }
      errorEnum(response, ErrorMessage.of(ErrorEnum.SYSTEM_ERROR, e.getMessage()));
    }
  }
}
